#include "log-remover.h"

#include <map>

#include "logger.h"


namespace logger {

LogRemover::LogRemover(const fs::path &logDir, std::chrono::seconds interval)
	: logDir_(logDir)
	, interval_(std::chrono::duration_cast<std::chrono::steady_clock::duration>(interval))
	, thread_([this] { loop(); })
{
}

LogRemover::~LogRemover()
{
	try {
		stopped_ = true;
		cv_.notify_all();
		thread_.join();
	}
	catch (...) {}
}

void LogRemover::loop()
{
	removeLog();

	while (!stopped_)
	{
		{
			std::unique_lock<std::mutex> lock(mtx_);
			cv_.wait_for(lock, interval_);
		}

		removeLog();
	}
}

static void removeFilesAndIgnoreErr(const std::vector<fs::path> &files)
{
	for (const fs::path &p : files) {
		try {
			fs::remove(p);
			LOGI << "LogRemover: file [" << p << "] removed";
		}
		catch (const std::exception &e) {
			// 忽略删除文件时遇到的错误，只需要记录日志即可
			LOGE << "LogRemover: remove file [" << p << "] failed because [" << e.what() << "]";
		}
	}
}

void LogRemover::removeLog()
{
	LOGI << "LogRemover::removeLog() dir: [" << logDir_ << "]";

	// 为了逻辑更健壮，假设同一个时间可能对应多个文件
	// 但实际上，通常一个时间只有一个文件
	std::map<fs::file_time_type, std::vector<fs::path>> logFiles;

	// 搜集日志文件的信息
	for (const fs::directory_entry &e : fs::directory_iterator(logDir_)) {
		logFiles[fs::last_write_time(e.path())].push_back(e.path());
	}

	// 确保最新的日志文件不被删除
	if (!logFiles.empty()) {
		logFiles.erase(logFiles.rbegin()->first);
	}

	constexpr int64_t KB = 1024;
	constexpr int64_t MB = 1024 * KB;
	constexpr int64_t GB = 1024 * MB;
	constexpr int64_t byteCountLimit = GB * 2;

	// 根据时间从后到前遍历文件，记录遇到的文件的总大小，如果发现达到了大小上限则标记 deleting
	// 在接下来的循环中，会因为标记了 deleting 而删除剩下的文件

	bool deleting = false;

	decltype(fs::file_size(fs::path())) byteCountSum = 0;

	for (auto it = logFiles.rbegin(); it != logFiles.rend(); ++it) {

		if (deleting) {
			removeFilesAndIgnoreErr(it->second);
			continue;
		}

		for (const fs::path &p : it->second) {
			byteCountSum += fs::file_size(p);
			LOGD << "LogRemover::removeLog() byteCountSum [" << byteCountSum << "], file [" << p << "]";
		}

		if (byteCountSum >= byteCountLimit) {
			deleting = true;
		}
	}
}

}

